package jp.tkym.labs.store.sql;

import java.util.EnumMap;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import jp.tkym.labs.store.ormap.OrMapElement;
import jp.tkym.labs.store.ormap.OrMapObject;
import jp.tkym.labs.store.ormap.OrMapObjectProvider;
import jp.tkym.labs.store.query.BeanQueryFilter;
import jp.tkym.labs.store.query.BeanQueryFilterOperator;
import jp.tkym.labs.store.query.BeanQuerySortDirection;
import jp.tkym.labs.store.query.BeanQuerySorter;

class SqliteCriteriaInterpreter<B>{
	private BeanQueryCriteriaHelper helper = new BeanQueryCriteriaHelper(); 
	private Map<String, String> orMap = new HashMap<String, String>();
	private Class<B> cls;
	
	SqliteCriteriaInterpreter(Class<B> cls){
		this.cls = cls;  
		OrMapObject orMapObject = OrMapObjectProvider.getInstance().get(cls);
		for(OrMapElement element : orMapObject.elements()){
			orMap.put(element.getPropertyName(), element.getColumnName());
		}
	}
	
	String whereStatement(List<BeanQueryFilter> filters){
		if(filters == null || filters.size() == 0) return "";
		StringBuilder sb = new StringBuilder();
		boolean isFirst = true;
		for(BeanQueryFilter filter : filters){
			if(isFirst){
				sb.append(" where ");
				isFirst = false;
			}else{
				sb.append(" and ");
			}
			sb.append(statementForFilter(filter));
		}
		return sb.toString();
	}
	
	String statementForFilter(BeanQueryFilter filter){
		String statement = 
			columnName(filter.getPropertyName()) + " " +
			helper.resolve(filter);
		return statement;
	}
	
	String orderByStatement(List<BeanQuerySorter> sorters){
		if(sorters == null || sorters.size() == 0) return "";
		StringBuilder sb = new StringBuilder();
		boolean isFirst = true;
		for(BeanQuerySorter sorter: sorters){
			if(isFirst){
				sb.append(" order by ");
				isFirst = false;
			}
			sb.append(statementForSorter(sorter));
		}
		return sb.toString();
	}
	
	String statementForSorter(BeanQuerySorter sorter){
		String statement = 
			columnName(sorter.getPropertyName()) + " " +
			direction(sorter.getSortOperation());
		return statement;
	}
	
	private String columnName(String propertyName){
		String columnName = orMap.get(propertyName);
		if(columnName!=null){
			return columnName;
		}else{
			String message = 
				"property " + propertyName + 
				" is not exists on class " + cls.getName();
			throw new IllegalArgumentException(message);
		}
	}
	
	private String direction(BeanQuerySortDirection direction){
		if(direction == BeanQuerySortDirection.DESCENDING) return "desc";
		if(direction == BeanQuerySortDirection.ASCENDING) return "asc";
		return "";
	}
	
	private class BeanQueryCriteriaHelper{
		private Map<BeanQueryFilterOperator, String> operatorMap;
		
		BeanQueryCriteriaHelper(){
			operatorMap = new EnumMap<BeanQueryFilterOperator, String>(BeanQueryFilterOperator.class); 
			operatorMap.put(BeanQueryFilterOperator.EQUAL, "=");
			operatorMap.put(BeanQueryFilterOperator.NOT_EQUAL, "<>");
			operatorMap.put(BeanQueryFilterOperator.GREATER_THAN, ">");
			operatorMap.put(BeanQueryFilterOperator.GREATER_THAN_OR_EQUAL, ">=");
			operatorMap.put(BeanQueryFilterOperator.LESS_THAN, "<");
			operatorMap.put(BeanQueryFilterOperator.LESS_THAN_OR_EQUAL, "<=");
			operatorMap.put(BeanQueryFilterOperator.IN, "in");
			operatorMap.put(BeanQueryFilterOperator.START_WITH, "like");
			operatorMap.put(BeanQueryFilterOperator.END_WITH, "like");
			operatorMap.put(BeanQueryFilterOperator.CONTAIN, "like");
		}
		
		String resolve(BeanQueryFilter filter){
			String append = " ?";
			if(filter.getFilterOperator().equals(BeanQueryFilterOperator.IN)){
				Object value = filter.getValue();
				Object[] values;
				if(value instanceof Object[]){
					values = (Object[])value;
				}else{
					values = new Object[1];
					values[1] = value;
				}
				int valueNum = values.length;
				append = bracketOff(valuesConnectComma(valueNum));
			}
			return operatorMap.get(filter.getFilterOperator()) + append;
		}
		
		String bracketOff(String str){
			return "(" + str + ")";
		}
		
		String valuesConnectComma(int size){
			boolean first = true;
			StringBuilder sb = new StringBuilder();
			for(int i=0; i<size; i++){
				if(first){
					first = false;
				}else{
					sb.append(", ");
				}
				sb.append("?");
			}
			return sb.toString();
		}
	}
}